/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package uk.co.dupenois.geometry;

import java.util.Comparator;

/**
 *
 * @author mpd209
 */
public class Edge extends Line{

    public Edge(Coordinate start, double angle, double length){
        super(start, angle, length, true);
    }
    public Edge(Coordinate start, Coordinate finish){
        super(start, finish, true);
    }
    public double getAngleAntiClockwiseFromMeTo(Edge other){
        return getAngleDirectedFromMe(other, false);
    }
    public double getAngleClockwiseFromMeTo(Edge other){
        return getAngleDirectedFromMe(other, true);
    }
    public double getAngleDirectedFromMe(Edge other, boolean clockwise){
        if(this.isParallel(other)) {
            return Math.PI;
        }
        Coordinate intersection;
        if(this.getStart().equalsCheck(other.getStart())||
                this.getStart().equalsCheck(other.getFinish())){
            intersection = this.getStart();
        }else if(this.getFinish().equalsCheck(other.getStart())||
                this.getFinish().equalsCheck(other.getFinish())){
            intersection = this.getFinish();
        }else{
            intersection = coincides(other, true);
        }

//        Coordinate coordinateDebug = new Coordinate(1593.0, 474.0);
//        boolean debug = (intersection.equalsCheck(coordinateDebug)||
//                this.getStart().equalsCheck(coordinateDebug)||
//                this.getFinish().equalsCheck(coordinateDebug)||
//                other.getStart().equalsCheck(coordinateDebug)||
//                other.getFinish().equalsCheck(coordinateDebug));
//
//        if(debug){
//            System.out.println("\t\tIntersection = "+intersection);
//            System.out.println("\t\tMy gradient = "+getSlope());
//            System.out.println("\t\tOther Slope = "+other.getSlope());
//        }
        /*
              \        ----a---\ O                 /
               i                \                 i
              O \                i            O  /
         ----a---\                \      ---a---/

              \        ----i---\
               a              O \
                \                a
         ----i---\ O              \


         * a = this
         * i = otherLine
         * If they don't coincide on segment doesn't matter which point from
         * start or finish we use, otherwise we want whichever point is not
         * equal to the intersection
         */
        Coordinate pointFromThis;
        Coordinate pointFromOther;
        if(this.getStart().equalsCheck(intersection)){
            pointFromThis = this.getFinish();
        }else{
            pointFromThis = this.getStart();
        }
        if(other.getStart().equalsCheck(intersection)){
            pointFromOther = other.getFinish();
        }else{
            pointFromOther = other.getStart();
        }
        Edge pointFromThisToIntersection = new Edge(pointFromThis, intersection);
//        if(debug){
//            System.out.println("\t\tPoint From This = "+pointFromThis);
//            System.out.println("\t\tPoint From Other = "+pointFromOther);
//        }
        /*
         * So if pointfromother is right of thisToIntersectionLine a
         * we want to use 2*math.pi -angle (other way round for anticlockwise)
        */


        //If point from other is left of this line we use the inner angle
        //If not we use the outer (2pi-angle) (2pi = 360deg), right if not clockwise
        double myAngle = intersection.getAngle(pointFromThis, pointFromOther);

        if(clockwise){
            if(pointFromThisToIntersection.isCoordinateLeftOfLine(pointFromOther)){
                return myAngle;
            }else{
                return (2*Math.PI)-myAngle;
            }
        }else{
            if(pointFromThisToIntersection.isCoordinateRightOfLine(pointFromOther)){

//                if(debug){
//                    System.out.println("\t\tRight Of line");
//                }
                return myAngle;
            }else{
//                if(debug){
//                    System.out.println("\t\tNot Right Of line");
//                }
                return (2*Math.PI)-myAngle;
            }
        }

    }
    public boolean isOnEdge(Coordinate point){
        if(!isOnLine(point)) return false;
        if(getStart().getX() == getFinish().getX()){
            if(isYNegative()){
                return (getFinish().getY()<=point.getY()&&point.getY()<=getStart().getY());
            }else{
                return (getStart().getY()<=point.getY()&&point.getY()<=getFinish().getY());
            }
        }else if(getStart().getY() == getFinish().getY()){
            if(isXNegative()){
                return (getFinish().getX()<=point.getX()&&point.getX()<=getStart().getX());
            }else{
                return (getStart().getX()<=point.getX()&&point.getX()<=getFinish().getX());
            }
        }else{
//            System.out.println("Start is "+(start==null?"":"NOT ")+"null");
//            System.out.println("Finish is "+(finish==null?"":"NOT ")+"null");
//            System.out.println("Point is "+(point==null?"":"NOT ")+"null");
            boolean between_y = false;
            boolean between_x = false;
            if(isYNegative()){
                between_y = (getFinish().getY()<=point.getY()&&point.getY()<=getStart().getY());
            }else{
                between_y = (getStart().getY()<=point.getY()&&point.getY()<=getFinish().getY());
            }
            if(isXNegative()){
                between_x = (getFinish().getX()<=point.getX()&&point.getX()<=getStart().getX());
            }else{
                between_x = (getStart().getX()<=point.getX()&&point.getX()<=getFinish().getX());
            }
            return(between_x&&between_y);
        }
    }

    @Override
    public String toString(){
        return getStart().toString()+"->"+getFinish().toString();
    }

    public String toString(int decimalPlaces){
        return getStart().toString(decimalPlaces)+"->"+getFinish().toString(decimalPlaces);
    }


    public final static CompareByCoordinatesMinXMinY ComparatorCoordinatesMinXMinY = new CompareByCoordinatesMinXMinY();
    public static class CompareByCoordinatesMinXMinY implements Comparator<Edge>{
        public int compare(Edge edge1, Edge edge2) {
            Coordinate edge1RightMost = edge1.getStart();
            Coordinate edge2RightMost = edge2.getStart();
            Coordinate edge1LeftMost = edge1.getFinish();
            Coordinate edge2LeftMost = edge2.getFinish();
            if(edge1LeftMost.compareByMaxXMinY(edge1RightMost)<0){
                edge1RightMost = edge1.getFinish();
                edge1LeftMost = edge1.getStart();
            }
            if(edge2LeftMost.compareByMaxXMinY(edge2RightMost)<0){
                edge2RightMost = edge2.getFinish();
                edge2LeftMost = edge2.getStart();
            }
            //Get diff between rightmost
            int diff = edge1RightMost.compareByMaxXMinY(edge2RightMost);
            if(diff==0){
                //If equal compare leftmost
                diff = edge1LeftMost.compareByMaxXMinY(edge2LeftMost);
            }
            //If equal now they're identical
            return diff;
        }
    }
    public final static CompareByCoordinatesMaxXMinY ComparatorCoordinatesMaxXMinY = new CompareByCoordinatesMaxXMinY();
    public static class CompareByCoordinatesMaxXMinY implements Comparator<Edge>{
        public int compare(Edge edge1, Edge edge2) {
            Coordinate edge1LeftMost = edge1.getStart();
            Coordinate edge2LeftMost = edge2.getStart();
            Coordinate edge1RightMost = edge1.getFinish();
            Coordinate edge2RightMost = edge2.getFinish();
            if(edge1RightMost.compareByMinXMinY(edge1LeftMost)<0){
                edge1LeftMost = edge1.getFinish();
                edge1RightMost = edge1.getStart();
            }
            if(edge2RightMost.compareByMinXMinY(edge2LeftMost)<0){
                edge2LeftMost = edge2.getFinish();
                edge2RightMost = edge2.getStart();
            }
            //Get diff between leftmost
            int diff = edge1LeftMost.compareByMinXMinY(edge2LeftMost);
            if(diff==0){
                //If equal compare rightmost
                diff = edge1RightMost.compareByMinXMinY(edge2RightMost);
            }
            //If equal now they're identical
            return diff;
        }
    }

}
